/**
 * @Project: JMC ( Javascript Memory Cache ).
 * @License: The MIT License.
 * @Author: Juan Pablo Guereca.
 * @Description: Not persistent standalone browserside key-value store with optional expiry,
 * 				 so useful for full ajax web applications.
 * @Interface:
 * 			- JMC.set( <namespace>, <key_name>, <value>, [expiry_seconds] )
 * 			- JMC.get( <namespace>, <key_name> ) returns value or null.
 * 			- JMC.remove( <namespace>, <key_name> ) return true or false.
 * 			- JMC.flush( [namespace] ). Resets the whole data stash or a specific namespace
 * 			- JMC.modify( <namespace>, <key_name>, <modifier> ).
 * 			- JMC.increase( <namespace>, <key_name> ) return null or true.
 * 			- JMC.decrease( <namespace>, <key_name> ) return null or true.
 */

var JMC = ( function ( ) {
	var _stash = {};
	/**
	 * Stores a value in a key in a namespace
	 * @param String namespace
	 * @param String key_name
	 * @param value
	 * @param Number expiry_seconds ( optional )
	 * @return true
	 */
	function set( namespace, key_name, value, expiry_seconds ) {
		if( typeof expiry_seconds !== 'undefined' ) {
			expiry_timestamp = ( (new Date).getTime() ) + ( expiry_seconds * 1000 );
		} else {
			expiry_timestamp = false;
		}
		if( ! ( namespace in _stash ) ) {
			_stash[namespace] = {};
		}
		_stash[namespace][key_name] = { 'value': value, 'expiry': expiry_timestamp };
		return true;
	}
	/**
	 * Retrieves the key value in a namespace
	 * @param String namespace
	 * @param String key_name
	 * @return value or null
	 */
	function get( namespace, key_name ) {
		if( !( namespace in _stash ) || !( key_name in _stash[namespace] ) ) {
			return null;
		}
		var stash_obj = _stash[namespace][key_name];
		if( stash_obj.expiry === false ) {
			return stash_obj.value;
		}
		var current_timestamp = (new Date).getTime();
		if( stash_obj.expiry < current_timestamp ) {
			delete _stash[namespace][key_name];
			return null;
		}
		return stash_obj.value;
	}

	/**
	 * Remove a key in a namespace
	 * @param String namespace
	 * @param String key_name
	 * @return true or false ( if it existed or not )
	 */
	function remove( namespace, key_name ) {
		if( ! ( namespace in _stash ) || ! ( key_name in _stash[namespace] ) ) {
			return false;
		}
		delete _stash[namespace][key_name];
		return true;
	}
	
	/**
	 * Flush the whole stash or a specific namespace
	 * @param String namespace ( optional ).
	 * @return true or false ( if it existed or not the namespace )
	 */
	function flush( namespace ) {
		var reset_obj;
		if( typeof namespace !== 'undefined' ) {
			if( namespace in _stash ) {
				delete _stash[namespace];
			} else {
				// The namespace doesn't exist.
				return false;
			}
		} else {
			_stash = {};
		}
		return true;
	}
	
	/**
	 * Increase the value 1 by default or the amount specified
	 * @param String namespace
	 * @param String key_name
	 * @param Number amount ( optional )
	 * @return null or true;
	 */
	function increase( namespace, key_name, amount ) {
		if( typeof amount === 'undefined' ) {
			amount = 1;
		}
		var value = get( namespace, key_name );
		if( null === value ) {
			return null;
		}
		set( namespace, key_name, value+amount );
		return true;
	}

	/**
	 * Decrease the value 1 by default or the amount specified
	 * @param String namespace
	 * @param String key_name
	 * @param Number amount ( optional )
	 * @return null or true
	 */
	function decrease( namespace, key_name, amount ) {
		if( typeof amount === 'undefined' ) {
			amount = -1;
		} else {
			amount *= -1;
		}
		var value = get( namespace, key_name );
		if( null === value ) {
			return null;
		}
		set( namespace, key_name, value+amount );
		return true;
	}
	
	/**
	 * Modify in place a key using a callback
	 * @param String namespace
	 * @param String key_name
	 * @param Function modifier
	 * @return null or true
	 */
	function modify( namespace, key_name, modifier ) {
		if( !( namespace in _stash ) || !( key_name in _stash[namespace] ) ) {
			return null;
		}
		_stash[namespace][key_name].value = modifier( _stash[namespace][key_name].value );
	}
	
	// Interface
	return {
			'set': set,
			'get': get,
			'remove': remove,
			'flush': flush,
			// For debugging purpose
			'_stash': _stash,
			'increase': increase,
			'decrease': decrease,
			'modify': modify
	}
} )( );